package com.sgzn.sgpay.util;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 身份证校验工具类
 * @author huangsenguang
 * @version Id: IdCardUtil.java, v 0.1 2018/6/4 22:38 huangsenguang Exp $$
 */
public class IdCardUtil {
    private static final String ACCEPT = ""; // 检查通过是返回的的成功标识字符串

    private static final int EIGHTEEN_IDCARD = 18;   //标识18位身份证号码
    private static final int FIFTEEN_IDCARD = 15;    //标识15位身份证号码

    private static final int MAX_MAINLAND_AREACODE = 659004; //大陆地区地域编码最大值
    private static final int MIN_MAINLAND_AREACODE = 110000; //大陆地区地域编码最小值
    private static final int HONGKONG_AREACODE = 810000; //香港地域编码值
    private static final int TAIWAN_AREACODE = 710000;   //台湾地域编码值
    private static final int MACAO_AREACODE = 820000;    //澳门地域编码值

    /**
     * 标识男性
     */
    public static final int MAN_SEX = 1;
    /**
     * 标识女性
     */
    public static final int WOMAN_SEX = 2;

    //从第一位到第十七位的乘法系数
    private static final int[] RATIOS = new int[]{7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};
    //储存18位身份证校验码
    private static final String[] CHECK_CODES = new String[]{"1","0","X","9","8","7","6","5","4","3","2"};

    private static String checkIdCardLen(String idCardInput) {
        if(idCardInput == null || "".equals(idCardInput)) {
            return "身份证号码为必填";
        }
        if(idCardInput.length() != 18 && idCardInput.length() !=15) {
            return "身份证号码位数不符";
        }
        return ACCEPT;
    }

    private static String checkIdCardLen(String idCardInput, int len) {
        if(idCardInput == null || idCardInput.length() != len) {
            return "身份证号码不合法";
        }
        return ACCEPT;
    }

    /**
     * 验证身份证号码是否符合法定编码规则，如果符合返回true，否则返回false。
     * 支持15位和18位的身份证号码验证。
     * @param idCardInput 身份证号码
     * @return
     */
    public static boolean checkIdCard(String idCardInput) {
        String lenCheckResult = checkIdCardLen(idCardInput);
        if(!ACCEPT.equals(lenCheckResult)) {
            return false;
        }
        if(idCardInput != null && idCardInput.length() == 18) {
            return checkIdCard18(idCardInput);
        }
        return checkIdCard15(idCardInput);
    }

    /**
     * 验证身份证号码是否符合法定编码规则，如果符合并且所输入性别和身份证号码对应的性别一致则返回true，否则返回false。
     * 支持15位和18位的身份证号码验证。
     * @param sex 性别
     * @param idCardInput 身份证号码
     * @return
     */
    public static boolean checkIdCard(int sex, String idCardInput) {
        String lenCheckResult = checkIdCardLen(idCardInput);
        if(!ACCEPT.equals(lenCheckResult)) {
            return false;
        }
        if(idCardInput != null && idCardInput.length() == 18) {
            return checkIdCard18(sex,idCardInput);
        }
        return checkIdCard15(sex, idCardInput);
    }

    private static boolean checkIdCardByCardType(int idCardType, String idCardInput) {
        if(!ACCEPT.equals(checkNumber(idCardType,idCardInput))) {
            return false;
        }
        if(!ACCEPT.equals(checkArea(idCardInput))) {
            return false;
        }
        if(!checkBirthDate(idCardType, idCardInput)) {
            return false;
        }
        return true;
    }

    /**
     * 验证15位身份证号码是否符合法定编码规则，如果符合则返回true，否则返回false。
     * @param idCardInput
     * @return
     */
    public static boolean checkIdCard15(String idCardInput) {
        if(!ACCEPT.equals(checkIdCardLen(idCardInput, 15))) {
            return false;
        }
        return checkIdCardByCardType(FIFTEEN_IDCARD, idCardInput);
    }

    /**
     * 验证15位身份证号码是否符合法定编码规则，如果符合并且所输入性别和身份证号码对应的性别一致则返回true，否则返回false。
     * @param sex
     * @param idCardInput
     * @return
     */
    public static boolean checkIdCard15(int sex,String idCardInput) {
        if(!checkIdCard15(idCardInput)) {
            return false;
        }
        if(!ACCEPT.equals(checkSeqCode(FIFTEEN_IDCARD,sex,idCardInput))) {
            return false;
        }
        return true;
    }

    /**
     * 验证18位身份证号码是否符合法定编码规则，如果符合则返回true，否则返回false。
     * @param idCardInput
     * @return
     */
    public static boolean checkIdCard18(String idCardInput) {
        if(!ACCEPT.equals(checkIdCardLen(idCardInput, 18))) {
            return false;
        }
        if (!checkIdCardByCardType(EIGHTEEN_IDCARD, idCardInput)) {
            return false;
        }
        if(!ACCEPT.equals(checkCheckCodeFor18(idCardInput))) {
            return false;
        }
        return true;
    }

    /**
     * * 验证18位身份证号码是否符合法定编码规则，如果符合并且所输入性别和身份证号码对应的性别一致则返回true，否则返回false。
     * @param sex
     * @param idCardInput
     * @return
     */
    public static boolean checkIdCard18(int sex, String idCardInput) {
        if(!checkIdCard18(idCardInput)) {
            return false;
        }
        if(!ACCEPT.equals(checkSeqCode(EIGHTEEN_IDCARD,sex,idCardInput))) {
            return false;
        }
        return true;
    }

    /**
     * 验证身份证的地域编码是符合规则
     */
    private static String checkArea(String idCardInput) {
        String subStr = idCardInput.substring(0, 6);
        int areaCode = Integer.parseInt(subStr);
        if(areaCode != HONGKONG_AREACODE && areaCode != TAIWAN_AREACODE
                && areaCode != MACAO_AREACODE
                && (areaCode > MAX_MAINLAND_AREACODE || areaCode < MIN_MAINLAND_AREACODE)) {
            return "输入的身份证号码地域编码不符合大陆和港澳台规则";
        }
        return ACCEPT;
    }

    /**
     * 验证身份证号码数字字母组成是否符合规则
     */
    private static String checkNumber(int idCardType ,String idCard) {
        char[] chars = idCard.toCharArray();
        if(idCardType == FIFTEEN_IDCARD) {
            for(int i = 0; i<chars.length;i++) {
                if(chars[i] > '9') {
                    return idCardType + "位身份证号码中不能出现字母";
                }
            }
        } else {
            for(int i = 0; i < chars.length; i++) {
                if(i < chars.length-1) {
                    if(chars[i] > '9') {
                        return EIGHTEEN_IDCARD + "位身份证号码中前" + (EIGHTEEN_IDCARD - 1) + "不能出现字母";
                    }
                } else {
                    if(chars[i] > '9' && chars[i] != 'X') {
                        return idCardType + "位身份证号码中最后一位只能是数字0~9或字母X";
                    }
                }
            }

        }

        return ACCEPT;
    }

    /**
     * 验证身份证号码出生日期是否符合规则
     */
    private static boolean checkBirthDate(int idCardType, String idCardInput) {
        if(!ACCEPT.equals(checkBirthYear(idCardType,idCardInput))) {
            return false;
        }
        if(!ACCEPT.equals(checkBirthMonth(idCardType,idCardInput))) {
            return false;
        }
        if(!ACCEPT.equals(checkBirthDay(idCardType,idCardInput))) {
            return false;
        }
        return true;
    }

    /**
     * 验证身份证号码出生日期年份是否符合规则
     */
    private static String checkBirthYear(int idCardType, String idCardInput) {
        if(idCardType == FIFTEEN_IDCARD) {
            int year = Integer.parseInt(idCardInput.substring(6, 8));
            if(year < 0 || year > 99) {
                return idCardType + "位的身份证号码年份须在00~99内";
            }
        } else {
            int year = Integer.parseInt(idCardInput.substring(6, 10));
            int yearNow = getYear();
            if(year < 1900 || year > yearNow) {
                return idCardType + "位的身份证号码年份须在1900~" + yearNow + "内";
            }
        }
        return ACCEPT;
    }

    /**
     * 验证身份证号码出生日期月份是否符合规则
     */
    private static String checkBirthMonth(int idCardType, String idCardInput) {
        int month = 0;
        if(idCardType == FIFTEEN_IDCARD) {
            month = Integer.parseInt(idCardInput.substring(8, 10));
        } else {
            month = Integer.parseInt(idCardInput.substring(10, 12));
        }
        if(month < 1 || month > 12) {
            return "身份证号码月份须在01~12内";
        }
        return ACCEPT;
    }

    /**
     * 验证身份证号码出生日期天数是否符合规则
     */
    private static String checkBirthDay(int idCardType, String idCardInput) {
        boolean bissextile = false;
        int year,month,day;
        if(idCardType == FIFTEEN_IDCARD) {
            year = Integer.parseInt("19"+idCardInput.substring(6, 8));
            month = Integer.parseInt(idCardInput.substring(8, 10));
            day = Integer.parseInt(idCardInput.substring(10, 12));
        } else {
            year = Integer.parseInt(idCardInput.substring(6, 10));
            month = Integer.parseInt(idCardInput.substring(10, 12));
            day = Integer.parseInt(idCardInput.substring(12, 14));
        }
        if(year%4 == 0 && year%100 != 0 || year%400 ==0) {
            bissextile = true;
        }

        switch(month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                if(day < 1 || day > 31) {
                    return "身份证号码大月日期须在1~31之间";
                }
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                if(day < 1 || day > 30) {
                    return "身份证号码小月日期须在1~30之间";
                }
                break;
            case 2:
                if(bissextile) {
                    if(day < 1 || day > 29) {
                        return "身份证号码闰年2月日期须在1~29之间";
                    }
                } else {
                    if(day < 1 || day > 28) {
                        return "身份证号码非闰年2月日期年份须在1~28之间";
                    }
                }
                break;
        }
        return ACCEPT;
    }

    /**
     * 验证身份证号码顺序码是否符合规则,男性为偶数,女性为奇数
     */
    private static String checkSeqCode(int idCardType , int sex, String idCardInput) {
        int sortCode = 0;
        if(idCardType == FIFTEEN_IDCARD) {
            sortCode = Integer.parseInt(idCardInput.substring(12, 15));
        } else {
            sortCode = Integer.parseInt(idCardInput.substring(14, 17));
        }

        if(sex == MAN_SEX) {
            if(sortCode%2 == 0) {
                return "男性的身份证顺序码须为奇数";
            }
        } else {
            if(sortCode%2 != 0) {
                return "女性的身份证顺序码须为偶数";
            }
        }

        return ACCEPT;
    }

    /**
     * 验证18位身份证号码校验码是否符合规则
     */
    private static String checkCheckCodeFor18(String idCard) {
        int sum = 0;
        char[] chars = idCard.toCharArray();
        int lastIndex = chars.length - 1;
        for(int i = 0; i < lastIndex; i++) {
            sum += (chars[i] - 48) * RATIOS[i];
        }
        int remainder = sum%11;
        String checkCode = CHECK_CODES[remainder];

        if(!checkCode.equals(String.valueOf(chars[lastIndex]))) {
            return "身份中的校验码不正确";
        }
        return ACCEPT;
    }

    /**
     * 返回当前年份
     */
    private static int getYear() {
        Date now = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyymmdd");
        String nowStr = format.format(now);
        return Integer.parseInt(nowStr.substring(0, 4));
    }
}